﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace Adams.Public.Api.Controllers
{
    [EnsureHttps]
    public class SecurityController : Controller
    {
        private const int _AllowedLoginAttempts = 5;
        private const int _LockoutIncrementInMinutes = 5;

        public ActionResult TryLogin(DataTransfer.Security.LoginInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.LoginOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (output.IsValid)
            {
                var data = DataAccess.Security.GetMemberInfo(input.Email); // Get User Information

                if (data.Exists)
                {
                    // User does exist
                    if (data.IsEmailConfirmed)
                    {
                        // User does exist
                        if (data.IsActive)
                        {
                            // User is active
                            // Check if account is locked
                            if (Adams.Shared.SecurityHelper.IsLocked(data.FailedLoginAttempts, data.LastLoginAttempt, _AllowedLoginAttempts, _LockoutIncrementInMinutes))
                            {
                                // User is locked out
                                output.IsLocked = true;
                                output.LockWaitMinutes = Adams.Shared.SecurityHelper.GetLockWaitMinutes(data.FailedLoginAttempts, data.LastLoginAttempt, _AllowedLoginAttempts, _LockoutIncrementInMinutes);
                                output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 22 });
                            }
                            else
                            {
                                // Check if password is valid
                                string hashedPassword = Adams.Shared.Encryption.HashPassword(input.Password, data.PasswordSalt);
                                bool isPasswordValid = Shared.Str.IsEqual(hashedPassword, data.PasswordHash);

                                if (isPasswordValid)
                                {
                                    // Login Successful
                                    DataAccess.Security.ClearLockout(data.MemberID); // Clear lockout info
                                    output.Token = Adams.Public.Api.Security.GenerateToken(data.MemberID);
                                }
                                else
                                {
                                    // Invalid password
                                    DataAccess.Security.UpdateLockout(data.MemberID); // Update lockout info
                                    data.FailedLoginAttempts++;
                                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Password", Code = 23 });

                                    if (data.FailedLoginAttempts >= _AllowedLoginAttempts)
                                    {
                                        output.IsLocked = true;
                                        output.LockWaitMinutes = Adams.Shared.SecurityHelper.GetLockWaitMinutes(data.FailedLoginAttempts, DateTime.UtcNow, _AllowedLoginAttempts, _LockoutIncrementInMinutes);
                                        output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 22 });
                                    }
                                    else
                                    {
                                        output.IsInvalidPassword = true;
                                    }

                                    output.AttemptsLeft = _AllowedLoginAttempts - data.FailedLoginAttempts;
                                }
                            }
                        }
                        else
                        {
                            output.IsNotActive = true;
                            output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 24 });
                        }
                    }
                    else
                    {
                        output.IsEmailNotConfirmed = true;
                        output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 19 });
                    }
                }
                else
                {
                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 20 });
                }
            }

            return new JsonXmlResult(output);
        }

        public ActionResult TrySignup(DataTransfer.Security.SignupInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.SignupOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (!Shared.Str.IsEqual(input.Password1, input.Password2))
            {
                output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Password2", Code = 6 });
            }

            if (output.IsValid)
            {
                var data = DataAccess.Security.GetMemberInfo(input.Email); // See if user exists

                if (!data.Exists)
                {
                    string passwordSalt = Shared.Encryption.GenerateSalt(16);
                    string passwordHash = Shared.Encryption.HashPassword(input.Password1, passwordSalt);

                    var save = Shared.ObjectHelper.CopyObject<DataTransfer.Security.SignupSaveData>(input);

                    save.PasswordHash = passwordHash;
                    save.PasswordSalt = passwordSalt;

                    DataAccess.Security.SaveMember(save);

                    string subject = "Confirm your ADAMS account, " + input.FirstName;
                    string from = Shared.Config.GetAppSetting("SecurityFromEmail");
                    string body = Api.Email.GenerateSignupEmail(input.Email);
                    Email.QueueEmail(subject, from, input.Email, body, 1);
                }
                else
                {
                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 21 });
                }
            }

            return new JsonXmlResult(output);
        }

        public ActionResult TryForgot(DataTransfer.Security.ForgotInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.ForgotOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (output.IsValid)
            {
                var data = DataAccess.Security.GetMemberInfo(input.Email); // Get User Information

                if (data.Exists)
                {
                    // User does exist
                    if (data.IsActive)
                    {
                        string subject = "Reset your ADAMS password";
                        string from = Shared.Config.GetAppSetting("SecurityFromEmail");
                        string body = Api.Email.GenerateForgotEmail(input.Email);
                        Email.QueueEmail(subject, from, input.Email, body, 1);
                    }
                    else
                    {
                        output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 24 });
                    }
                }
                else
                {
                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 20 });
                }
            }

            return new JsonXmlResult(output);
        }

        public ActionResult TrySignupComplete(DataTransfer.Security.SignupCompleteInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.SignupCompleteOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (output.IsValid)
            {
                string email = Api.Email.GetEmailFromCode(input.Code);
                DataAccess.Security.ConfirmEmail(email);
                output.IsSaved = true;
            }

            return new JsonXmlResult(output);
        }

        public ActionResult TryPasswordReset(DataTransfer.Security.PasswordResetInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.PasswordResetOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (output.IsValid)
            {
                if (!Shared.Str.IsEqual(input.Password1, input.Password2))
                {
                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Password2", Code = 6 });
                }

                if (output.IsValid)
                {
                    string email = Api.Email.GetEmailFromCode(input.Code);
                    var data = DataAccess.Security.GetMemberInfo(email);
                    string hashedPassword = Adams.Shared.Encryption.HashPassword(input.Password1, data.PasswordSalt);
                    DataAccess.Security.ResetPassword(email, hashedPassword);
                    output.IsSaved = true;

                    string subject = "Your password has been reset";
                    string from = Shared.Config.GetAppSetting("SecurityFromEmail");
                    string body = Api.Email.GeneratePasswordResetCompleteEmail(email);
                    Email.QueueEmail(subject, from, email, body, 1);
                }
            }

            return new JsonXmlResult(output);
        }

        public ActionResult TryResend(DataTransfer.Security.ResendInput input)
        {
            var output = new Adams.Public.DataTransfer.Security.ResendOutput();
            Validation.PerformBasicValidation(input, (Adams.Public.DataTransfer.FormOutput)output);

            if (output.IsValid)
            {
                var data = DataAccess.Security.GetMemberInfo(input.Email); // Get User Information

                if (data.Exists)
                {
                    // User's email is not confirmed
                    if (data.IsEmailConfirmed)
                    {
                        output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 28 });
                    }
                    else
                    {
                        string subject = "Confirm your ADAMS account - Resent";
                        string from = Shared.Config.GetAppSetting("SecurityFromEmail");
                        string body = Api.Email.GenerateSignupEmail(input.Email);
                        Email.QueueEmail(subject, from, input.Email, body, 1);
                    }
                }
                else
                {
                    output.InvalidFields.Add(new DataTransfer.InvalidField() { Name = "Email", Code = 20 });
                }
            }

            return new JsonXmlResult(output);
        }
    }
}
